--  ______________________________
-- | ____________________________ |
-- | |      -( VSCycles )-      | |
-- | | Special Commented Source | |
-- | |           v1.0           | |
-- | |   An NGDesigns Creation  | |
-- | |__________________________| |
-- | A 2D Tron clone for the PSP. |
-- |        Written in Lua.       |
-- |______________________________|
-- With source from "Rotate" sample
-- provided with LuaPlayer package.

-- Activate USB access mode
System.usbDiskModeActivate()

-- Version Number
Version = "v1.0"

-- -----------------------------------
-- "Rotate.lua" sample functions
-- Used for displaying player scores.
-- -----------------------------------

function rotate(image)
	local w = image:width()
	local h = image:height()
	local result = Image.createEmpty(h, w)
	for x=0,w-1 do
		for y=0,h-1 do
			result:pixel(h-y-1, x, image:pixel(x, y))
		end
	end
	return result
end

function printRotated(x, y, text, color, image, rotateIndex)
	rotateIndex = math.mod(rotateIndex, 4)
	local w = string.len(text)
	local result = Image.createEmpty(w * 8, 8)
	result:print(0, 0, text, color)
	if rotateIndex > 0 then
		rotateIndex = rotateIndex - 1
		for i=0,rotateIndex do
			result = rotate(result)
		end
	end
	image:blit(x, y, result)
end

-- End "Rotate.lua" functions

-- ----------------------
-- Define Colors + Images
-- ----------------------
white = Color.new(255, 255, 255)
black = Color.new(0, 0, 0)
bgcolor = Color.new(0, 0, 0)
red = Color.new(255, 0, 0)
blue = Color.new(0, 0, 255)
green = Color.new(0, 255, 0)
background = Image.load("br_back.png")
initbg = Image.load("br_back.png")
player1wins = Image.load("blue_wins.png")
player2wins = Image.load("red_wins.png")
player1gain = Image.load("bluegain.png")
player1lose = Image.load("bluelose.png")
player2gain = Image.load("redgain.png")
player2lose = Image.load("redlose.png")
playerdraw = Image.load("draw.png")
player = Image.createEmpty(480, 272)
p2color = Color.new(255, 0, 0)
p1color = Color.new(0, 0, 255)
menubg = Image.load("menubg.png")
logo = Image.load("logo0.png")
cursor = Image.load("cursor.png")
options = Image.load("options.png")
howto1 = Image.load("howto1.png")
howto2 = Image.load("howto2.png")

-- ------------------------
-- Define Initial Variables
-- ------------------------

-- Activate Menu variable
menu = true

-- Initial Menu selection (1-4)
choice = 1

-- Used for main game loop. Set to false will end game.
game = true

-- Play Area Corners
corner_x1 = 20
corner_y1 = 10
corner_x2 = 460
corner_y2 = 265

-- Draw Playing Field
background:fillRect(corner_x1, corner_y1, (corner_x2 - corner_x1 + 1), (corner_y2 - corner_y1 + 1), black)
background:drawLine(corner_x1 - 1, corner_y1 - 1, corner_x1 - 1, corner_y2 + 1, white)
background:drawLine(corner_x1 - 1, corner_y1 - 1, corner_x2 + 1, corner_y1 - 1, white)
background:drawLine(corner_x2 + 1, corner_y1 - 1, corner_x2 + 1, corner_y2 + 1, white)
background:drawLine(corner_x1 - 1, corner_y2 + 1, corner_x2 + 1, corner_y2 + 1, white)

-- Initial Player Scores
p1_score = 0
p2_score = 0

-- Score to win (Initial at 5, change in Options)
win_score = 5

--Game Speed (15 = normal, 0 = slowest, 20 = fastest)
speed = 15

-- Pixel step - defines number of pixels moved
-- each cycle. Adjustments will alter game speed.
-- !BUGGY! Adaptive corner/player locations have
-- not yet been implemented and adjustments to
-- this value will negatively affect gameplay.
step = 5

-- ----------------------------------
-- Reset screen and player variables,
-- draw scores and blank backgrounds
-- ----------------------------------
function Reset()

--Clear Active Player layer
player = Image.createEmpty(480, 272)

--Multiplier to correct game speed when input function is run
move = 1

--Display Scores on Active Player layer
printRotated(5, 86 , p1_score, black, player, 1)
printRotated(5, 217 , p2_score, black, player, 1)
printRotated(467, 171 , p2_score, black, player, 3)
printRotated(467, 40 , p1_score, black, player, 3)

--Reset Player locations and directions
p1_x = corner_x1 + 30
p1_y = (corner_y2 - corner_y1) / 2 + corner_y1
oldp1_x = p1_x
oldp1_y = p1_y
p1_up = 0
p1_down = 0
p1_right = step
p1_left = 0

p2_x = corner_x2 - 30
p2_y = (corner_y2 - corner_y1) / 2 + corner_y1
oldp2_x = p2_x
oldp2_y = p2_y
p2_up = 0
p2_down = 0
p2_right = 0
p2_left = step

-- Show blank background
screen:blit(0, 0, background)
screen.waitVblankStart()
screen.flip()

-- Color variables
color_up = true
setcolor = 0
cbase = 255

end

-- --------------------------------------
-- Sets player colors for gradient effect
-- --------------------------------------
function Set_Player_Colors()
  --Moves a counter up and down between 0 and 200
  if color_up and setcolor <= 200 then
	setcolor = setcolor + 10
  end
  
  if setcolor >= 200 then
    color_up = false
  end
  
  if color_up == false and setcolor >= 10 then
    setcolor = setcolor - 10
  end
  
  if setcolor == 0 then
    color_up = true
  end
  
  -- Sets Player colors with the counter value
  p2color = Color.new(cbase, setcolor, setcolor)
  p1color = Color.new(setcolor, setcolor, cbase)
end
  
-- ----------------------------------------------------------
-- Tests for Active Line presence and returns a boolean value
-- True = No active line in player's path
-- False = Active line in player's path
-- ----------------------------------------------------------
function NoActiveLine(x, y)
  if background:pixel(x, y) == black then
  pct = player:pixel(x, y)
  pctc = pct:colors()
  if pctc.b == p1color:colors().b or pctc.r == p2color:colors().r then
  return false
  else
  return true
  end
  else
  return false
  end
end

-- -------------------------------------------------------------------
-- Tests for Specific Active Line presence and returns a boolean value
-- False = No active line of tested color in player's path
-- True = Active line of tested color in player's path 
-- -------------------------------------------------------------------
function ActiveLine_Specific(x, y, test_color)
  pct = player:pixel(x, y)
  pctc = pct:colors()
  tc = test_color:colors()
  if test_color == p1color and pctc.b == tc.b then
  return true
  elseif test_color == p2color and pctc.r == tc.r then
  return true
  else
  return false
  end
end


-- --------------------------------------
-- Draws additional Active Lines onscreen
-- --------------------------------------
function Feed_Active()
--Changes player colors
  Set_Player_Colors()
--Draw to player layer
  player:drawLine(oldp1_x, oldp1_y, p1_x, p1_y, p1color)
  player:drawLine(oldp2_x, oldp2_y, p2_x, p2_y, p2color)
--Store locations for next draw
  oldp2_x = p2_x
  oldp2_y = p2_y
  oldp1_x = p1_x
  oldp1_y = p1_y

--Wait timer - based on Speed variable  
  wait = 0
  if speed > 20 then speed = 20 end
  while wait < (20000 - (speed * 1000)) do
  wait = wait + 1
  end
  
--Draw Player layer to Screen
  screen:blit(0, 0, background)
  screen:blit(0, 0, player)
  screen.waitVblankStart()
  screen:flip()
end

-- ------------------------------------------------------
-- Test for Player 1 Input and change player direction
-- -Input is tested in this fashion with "elseif" so that
--  multiple input from Player 1 does not take priority
--  over Player 2 input
-- ------------------------------------------------------
function Player1_Input()
  --Pad UP pressed or Pad DOWN pressed
  if NewInput.read():up() then
    if p1_right == step then
      p1_right = 0
      p1_up = step
    elseif p1_up == step then
      p1_up = 0
      p1_left = step
    elseif p1_left == step then
      p1_left = 0
      p1_down = step
    elseif p1_down == step then
      p1_down = 0
      p1_right = step
    end
  elseif NewInput.read():down() then
    if p1_right == step then
      p1_right = 0
      p1_down = step
     elseif p1_up == step then
      p1_up = 0
      p1_right = step
    elseif p1_left == step then
      p1_left = 0
      p1_up = step
    elseif p1_down == step then
      p1_down = 0
      p1_left = step
    end
  end
end

-- ------------------------------------------------------
-- Test for Player 2 Input and change player direction
-- -Input is tested in this fashion with "elseif" so that
--  multiple input from Player 2 does not take priority
--  over Player 1 input. Requires "and not" to prevent
--  multiple directions at once due to P2 control type.
-- ------------------------------------------------------
function Player2_Input()
  -- Button (^) pressed or Button (X) pressed
  if NewInput.read():cross() and not NewInput.read():triangle() then
    if p2_right == step then
      p2_right = 0
      p2_up = step
    elseif p2_up == step then
      p2_up = 0
      p2_left = step
    elseif p2_left == step then
      p2_left = 0
      p2_down = step
    elseif p2_down == step then
      p2_down = 0
      p2_right = step
    end
  elseif NewInput.read():triangle() and not NewInput.read():cross() then
    if p2_right == step then
      p2_right = 0
      p2_down = step
     elseif p2_up == step then
      p2_up = 0
      p2_right = step
    elseif p2_left == step then
      p2_left = 0
      p2_up = step
    elseif p2_down == step then
      p2_down = 0
      p2_left = step
    end
  end
end

-- ---------------------------------------------------
-- Tests the circumstances of a collision for Player 1
-- and determines the correct course of action.
-- ---------------------------------------------------
function P1_Collision_Test()
--Is Player 1 colliding with self? If so, Player 1 loses a point
  if ActiveLine_Specific(p1_x, p1_y, p1color) or background:pixel(p1_x, p1_y) ~= black then
    P1_LosePoint()
--Is Player 1 colliding with Player 2? If so, Player 2 gains a point
  elseif ActiveLine_Specific(p1_x, p1_y, p2color) then
    P2_GainPoint()
  end
end

-- ---------------------------------------------------
-- Tests the circumstances of a collision for Player 2
-- and determines the correct course of action.
-- ---------------------------------------------------
function P2_Collision_Test()
--Is Player 2 colliding with self? If so, Player 2 loses a point
  if ActiveLine_Specific(p2_x, p2_y, p2color) or background:pixel(p2_x, p2_y) ~= black then
    P2_LosePoint()
--Is Player 2 colliding with Player 1? If so, Player 1 gains a point
  elseif ActiveLine_Specific(p2_x, p2_y, p1color) then
    P1_GainPoint()
  end
end

-- ------------------------------------------------
-- Tests the circumstances when both Player 1 and 2
-- are in a collision simultaneously.
-- ------------------------------------------------
function Multiple_Collision_Test()
--Is Player 1 colliding with self and Player 2 colliding with Player 1? Player 1 loses a point
  if ActiveLine_Specific(p1_x, p1_y, p1color) and ActiveLine_Specific(p2_x, p2_y, p1color) then
    P1_LosePoint()
--Is Player 1 colliding with self and Player 2 colliding with self? Draw, no points to either player
  elseif ActiveLine_Specific(p1_x, p1_y, p1color) and ActiveLine_Specific(p2_x, p2_y, p2color) then
    No_Points()
--Is Player 1 colliding with Player 2 and Player 2 colliding with self? Player 2 loses a point
  elseif ActiveLine_Specific(p1_x, p1_y, p2color) and ActiveLine_Specific(p2_x, p2_y, p2color) then
    P2_LosePoint()
--Is Player 1 leaving the playfield and Player 2 colliding with line? Draw, no points to either player
  elseif background:pixel(p1_x, p1_y) ~= black and NoActiveLine(p2_x, p2_y) == false then
    No_Points()
--Is Player 2 leaving the playfield and Player 1 colliding with line? Draw, no points to either player
  elseif background:pixel(p2_x, p2_y) ~= black and NoActiveLine(p1_x, p1_y) == false then
    No_Points()
--Are both players leaving the playfield? Draw, no points to either player
  elseif background:pixel(p1_x, p1_y) ~= black and background:pixel(p2_x, p2_y) ~= black then
    No_Points()
--Are both players colliding with each other?
  elseif ActiveLine_Specific(p1_x, p1_y, p2color) and ActiveLine_Specific(p2_x, p2_y, p1color) then
    No_Points()
  end
end

-- ---------------------------
-- Add point to Player 1 Score
-- ---------------------------
function P1_GainPoint()
--Draw the final player lines
  Feed_Active()
  p1_score = p1_score + 1
--If the winning score is reached, go to Game Over, otherwise...
  if p1_score == win_score then
    Game_Over()
  else
--Show the Gain Point graphic, wait, and restart
  screen:blit(0, 0, background)
  screen:blit(0, 0, player)
  screen:blit(0, 0, player1gain)
  screen:flip()
  Wait(7)
  Reset()
  end
end

-- ---------------------------
-- Add point to Player 2 Score
-- ---------------------------
function P2_GainPoint()
--Draw the final player lines
  Feed_Active()
  p2_score = p2_score + 1
--If the winning score is reached, go to Game Over, otherwise...
  if p2_score == win_score then
    Game_Over()
  else
--Show the Gain Point graphic, wait, and restart
  screen:blit(0, 0, background)
  screen:blit(0, 0, player)
  screen:blit(0, 0, player2gain)
  screen:flip()
  Wait(7)
  Reset()
  end
end

-- ---------------------------
-- Sub point to Player 1 Score
-- ---------------------------
function P1_LosePoint()
--Draw the final player lines
  Feed_Active()
  if p1_score > 0 then
    p1_score = p1_score - 1
  end
--Show the Lose Point graphic, wait, and restart
  screen:blit(0, 0, background)
  screen:blit(0, 0, player)
  screen:blit(0, 0, player1lose)
  screen:flip()
  Wait(7)
  Reset()
end

-- ---------------------------
-- Sub point to Player 2 Score
-- ---------------------------
function P2_LosePoint()
--Draw the final player lines
  Feed_Active()
  if p2_score > 0 then
    p2_score = p2_score - 1
  end
--Show the Lose Point graphic, wait, and restart
  screen:blit(0, 0, background)
  screen:blit(0, 0, player)
  screen:blit(0, 0, player2lose)
  screen:flip()
  Wait(7)
  screen:clear()
  Reset()
end

-- ----------------------------------------
-- Player Draw - No points to either player
-- ----------------------------------------
function No_Points()
--Draw the final player lines and show No Points graphic
  Feed_Active()
  screen:blit(0, 0, background)
  screen:blit(0, 0, player)
  screen:blit(0, 0, playerdraw)
  screen:flip()
  Wait(7)
  Reset()
end

-- ------------------------------
-- 'Game Over' screen border draw
-- ------------------------------
function GO_DrawBorder()
    screen:blit(0, 0, menubg)
    screen:fillRect(15, 15, 175, 242, white)
    screen:fillRect(290, 15, 175, 242, white) 
    screen:drawLine(15, 15, 190, 15, black)
    screen:drawLine(15, 15, 15, 257, black)
    screen:drawLine(190, 15, 190, 257, black)
    screen:drawLine(15, 257, 190, 257, black)  
    screen:drawLine(290, 15, 465, 15, black)
    screen:drawLine(290, 15, 290, 257, black)
    screen:drawLine(290, 257, 465, 257, black)
    screen:drawLine(465, 15, 465, 257, black)
end

-- ---------------------------------------
-- Displays appropriate 'Game Over' screen
-- and waits for input to restart or quit
-- ---------------------------------------
function Game_Over()
--If Player 1 wins, show Player 1 Wins graphic
  if p1_score == win_score then
    GO_DrawBorder()
    screen:blit(0, 0, player1wins)
    screen:flip()
  end
--If Player 2 wins, show Player 2 Wins graphic
  if p2_score == win_score then
    GO_DrawBorder()
    screen:blit(0, 0, player2wins)
    screen:flip()
  end
--Wait for input from players
  wait = true
  while wait do
--If pad RIGHT pressed, then start a new game
    if Controls.read():right() then
      wait = false
    end
--If button SQUARE pressed, then start a new game
    if Controls.read():square() then
      wait = false
    end
--If pad LEFT pressed, return to menu
    if Controls.read():left() then
      menu = true
      choice = 1
      wait = false
    end
--If button CIRCLE pressed, return to menu
    if Controls.read():circle() then
      menu = true
      choice = 1
      wait = false
    end
  end
--Reset scores and call Reset function before starting
--a new game or returning to the menu screen
  p1_score = 0
  p2_score = 0
  Reset()
end

-- ------------------------------------------------------
-- Menu Draw - Prints version number and draws the cursor
-- ------------------------------------------------------
function Draw_Menu()
  screen:blit(0, 0, logo)
  screen:print(435, 258, Version, white)
  if choice == 1 then screen:blit(165, 143, cursor) end
  if choice == 2 then screen:blit(160, 173, cursor) end
  if choice == 3 then screen:blit(180, 202, cursor) end
  if choice == 4 then screen:blit(197, 230, cursor) end
  screen:flip()
end


-- ----------
-- Wait Timer - Waits a period based on
-- passed variable and returns to the game.
-- ----------
function Wait(duration)
wt = 0
while wt < (duration * 18) do
if Controls.read():start() then
  menu = true
  wt = (duration * 18)
  end
wt = wt + 1
end
end
-- ========================
-- =----------------------=
-- =- Main Game Function -=
-- =----------------------=
-- ========================

--Do the following until menu 'Exit' is selected
while game do

-- ===============
-- =-------------=
-- =- Menu Code -=
-- =-------------=
-- ===============

--Do the following section while menu is active
while menu do
--Draw the main menu
  Draw_Menu()
--Allow menu selection until player presses button CROSS
  while not Controls.read():cross() do
--If menu selection is not at the top, allow the cursor to move up and draw menu
    if Controls.read():up() and choice > 1 then
      choice = choice - 1
      Draw_Menu()
      while Controls.read():up() do end
    end
--If menu selection is not at the bottom, allow the cursor to move down and draw menu
    if Controls.read():down() and choice < 4 then
      choice = choice + 1
      Draw_Menu()
      while Controls.read():down() do end
    end
  end
--If user selects choice 1 'New Game', start a new game
  if choice == 1 then
    --Display countdown to new game
    screen:blit(0, 0, initbg)
	screen:print(330, 153, "3", white)
	screen:flip()
	Wait(6)
	screen:blit(0, 0, initbg)
	screen:print(330, 153, "2", white)
	screen:flip()
	Wait(6)
	screen:blit(0, 0, initbg)
	screen:print(330, 153, "1", white)
	screen:flip()
	Wait(6)
	--Reset variables
	Reset()
	OldInput = Controls.read()
    menu = false
  end
--If user selects choice 2 'How to Play', show How to Play graphics
  if choice == 2 then
    while Controls.read():cross() do end
    screen:blit(0, 0, menubg)
    screen:blit(20, 20, howto1)
    screen:flip()
    while not Controls.read():cross() do end
    screen:blit(0, 0, menubg)
    screen:blit(20, 20, howto2)
    screen:flip()
    while Controls.read():cross() do end
    while not Controls.read():cross() do end
    choice = 1
  end
--If user selects choice 3 'Option', display Options menu
  if choice == 3 then
    while Controls.read():cross() do end
    while not Controls.read():cross() do
--Display current options
      if win_score == 5 then
        screen:blit(0, 0, menubg)
        screen:blit(0, 0, options)
        screen:blit(185, 60, cursor)
        screen:print(200, 112, speed, black)
        screen:flip()
      elseif win_score == 10 then
        screen:blit(0, 0, menubg)
        screen:blit(0, 0, options)
        screen:blit(225, 60, cursor)
        screen:print(200, 112, speed, black)
        screen:flip()
      elseif win_score == 15 then
        screen:blit(0, 0, menubg)
        screen:blit(0, 0, options)
        screen:blit(275, 60, cursor)
        screen:print(200, 112, speed, black)
        screen:flip()
      end
--If user presses pad RIGHT and the win score is not at max, then move cursor right      
      if Controls.read():right() and win_score < 15 then
        win_score = win_score + 5
        while Controls.read():right() do end
      end
--If user presses pad LEFT and the win score is not at minimum, then move cursor left      
      if Controls.read():left() and win_score > 5 then
        win_score = win_score - 5
        while Controls.read():left() do end
      end
--If user presses pad UP and the game speed is not at max, add one to speed      
      if Controls.read():up() and speed < 20 then
        speed = speed + 1
        while Controls.read():up() do end
      end
--If user presses pad DOWN and the game speed is not at minimum, sub one to speed      
      if Controls.read():down() and speed > 0 then
        speed = speed - 1
        while Controls.read():down() do end
      end
    end
  choice = 1
  end
--If user selects choice 4 'Exit', end the game  
  if choice == 4 then
    OldInput = Controls.read()
    Reset()
    game = false
    menu = false
    end
while Controls.read():cross() do end
end

-- ===============
-- =-------------=
-- =- Game Code -=
-- =-------------=
-- ===============

  -- Test for new input. Perform only input functions
  -- where input has changed speeds up the response time.
  NewInput = Controls.read()
  move = 1
  if NewInput ~= OldInput then
	if NewInput:up() ~= OldInput:up() or NewInput:down() ~= OldInput:down() then
		Player1_Input()
	end
	if NewInput:triangle() ~= OldInput:triangle() or NewInput:cross() ~= OldInput:cross() then
		Player2_Input()
    end
    OldInput = NewInput
    --Correct slowdown for running input functions
    move = 2
  end
--Test for START button press to return to menu  
  if Controls.read():start() then menu = true end
  
  -- Move the current player locations
  p1_x = p1_x + p1_right - p1_left
  p1_y = p1_y - p1_up + p1_down
  
  p2_x = p2_x + p2_right - p2_left
  p2_y = p2_y - p2_up + p2_down
  
  -- Are both players attempting to move to same spot? Must be tested for first because
  -- the method used in Multiple Collision test function will not detect it, and will
  -- not return false in tests required for Feed_Active.
  if p1_x == p2_x and p1_y == p2_y then
    No_Points()
  -- Are there no lines in the way of either players path?
  elseif NoActiveLine(p1_x, p1_y) and NoActiveLine(p2_x, p2_y) then
    Feed_Active()
  -- Is Player 1 colliding with another line and Player 2 is not?
  elseif NoActiveLine(p1_x, p1_y) == false and NoActiveLine(p2_x, p2_y) then
    P1_Collision_Test()
  -- Is Player 2 colliding with another line and Player 1 is not?
  elseif NoActiveLine(p1_x, p1_y) and NoActiveLine(p2_x, p2_y) == false then
    P2_Collision_Test()
  -- Are both Player 1 and 2 colliding with lines?
  elseif NoActiveLine(p1_x, p1_y) == false and NoActiveLine(p2_x, p2_y) == false then
    Multiple_Collision_Test()
  end
end

--After game end
